%%%-------------------------------------------------------------------
%%% File    : rq_server.erl
%%% Author  : Lucas Carlson <lucas@mog.com>
%%% Description : 
%%%
%%% Created :  31 July 2007 by Lucas Carlson <lucas@mog.com>
%%%-------------------------------------------------------------------
-module(rq_server).

-behaviour(gen_server).

%% API
-export([start_updating/1, start_link/0, update/0, status/0]).
-export([queue_sizes/0, total_queue_size/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

%%--------------------------------------------------------------------
%% record definitions
%%--------------------------------------------------------------------

-record(state, {client_queues = []}).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
  gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

update() ->
	gen_server:cast(?MODULE, update).

status() ->
	gen_server:cast(?MODULE, status).

queue_sizes() ->
	gen_server:call(?MODULE, queue_sizes).

total_queue_size() ->
	gen_server:call(?MODULE, total_queue_size).	

start_updating(Timeout) ->
	receive after Timeout -> update() end,
	start_updating(Timeout).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
	spawn_link(?MODULE, start_updating, [2000]),
    {ok, #state{}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(queue_sizes, _From, State) ->
	{reply, State#state.client_queues, State};

handle_call(total_queue_size, _From, State) ->
	TotalQueueSize = lists:foldl(fun({_Node, R}, Total) -> case R of timeout -> Total; R -> Total + R end end, 0, State#state.client_queues),
	{reply, TotalQueueSize, State};

handle_call(Request, From, State) ->
	error_logger:error_msg("Invalid call (rq_server): ~w (~w)~n", [Request, From]),
    {reply, ok, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(update, State) ->
	NewClientQueues = lists:map(fun({Node, Pid, _Weight, _R, _Q}) ->
		Pid ! {requests, self()},
		receive
			{requests_response, N} when integer(N) ->
				{Node, N};
			_ ->
				{Node, 0}
		after 1000 ->
			{Node, timeout}
		end
	end, dp_server:clients()),
	dp_server:client_queues(NewClientQueues),
	{noreply, State#state{client_queues = NewClientQueues}};

handle_cast(status, State) ->
	error_logger:info_msg("Requests queue size per client: ~w~n", [State#state.client_queues]),
	TotalQueueSize = lists:foldl(fun({_Node, R}, Total) -> case R of timeout -> Total; R -> Total + R end end, 0, State#state.client_queues),
	error_logger:info_msg("Total queue size: ~w~n", [TotalQueueSize]),
	case TotalQueueSize > length(State#state.client_queues) of
		true ->
			error_logger:info_msg("UNDER CAPACITY~n");
		false ->
			error_logger:info_msg("OVER CAPACITY~n")
	end,
	{noreply, State};
	
handle_cast(Msg, State) ->
	error_logger:error_msg("Invalid cast (rq_server): ~w~n", [Msg]),
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------